package com.xneure.database;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import com.xneure.database.dao.Paging;
import com.xneure.utils.ObjectUtil;

@Intercepts({@Signature(type = StatementHandler.class,method = "prepare",args = {Connection.class,Integer.class})})
public class SqlPrepInterceptor implements Interceptor{
	@SuppressWarnings("rawtypes")
	@Override
	public Object intercept(Invocation invocation) throws Throwable{
		StatementHandler stmtHandler = getUnProxyObject(invocation);
		MetaObject metaStatementHandler = SystemMetaObject.forObject(stmtHandler);
		BoundSql boundSql = (BoundSql)metaStatementHandler.getValue("delegate.boundSql");
		String sql = boundSql.getSql();
		Object parameterObject = boundSql.getParameterObject();
		Paging page = null;
		// 生成查询sql
		if(parameterObject instanceof Map){
			Map<?,?> paramMap = (Map<?,?>)parameterObject;
			if(ObjectUtil.isNull(sql)){
				for(Map.Entry<?,?> ent:paramMap.entrySet()){
					if(ent.getValue() instanceof String){
						if(ent.getValue().toString().toLowerCase().indexOf(" from ") > -1){
							sql = ent.getValue().toString();
						}
					}else if(ent.getValue() instanceof Paging){
						page = (Paging)ent.getValue();
					}
					if(ObjectUtil.isNotNull(sql) && null != page){
						break;
					}
				}
			}else{
				Map<?,?> parMap = (Map)paramMap;
				for(Entry key:parMap.entrySet()){
					if(key.getValue() instanceof Paging){
						page = (Paging)key.getValue();
						break;
					}
				}
			}
		}else if(parameterObject instanceof Paging){
			page = (Paging)parameterObject;
		}
		if(null != page){
			setPageParameter(sql,(Connection)invocation.getArgs()[0],page,boundSql,metaStatementHandler);
			sql = DynamicDataSource.getSqlDialect().paging(sql,page.getPageNum() * page.getPageSize(),page.getPageSize());
		}
		if(ObjectUtil.isNotNull(sql)){
			metaStatementHandler.setValue("delegate.boundSql.sql",sql);
		}
		return invocation.proceed();
	}

	/**
	 * 获取总记录数
	 * 
	 * @param sql
	 * @param connection
	 * @param mappedStatement
	 * @param boundSql
	 * @param page
	 * @param boundSql
	 */
	private void setPageParameter(String sql,Connection connection,Paging page,BoundSql boundSql,MetaObject metaStatementHandler){
		// 记录总记录数
		String countSql = "select count(1) from (".concat(sql).concat(") total");
		ResultSet rs = null;
		PreparedStatement countStmt = null;
		try{
			MappedStatement mappedStatement = (MappedStatement)metaStatementHandler.getValue("delegate.mappedStatement");
			// 获取所有配置Configuration
			countStmt = connection.prepareStatement(countSql);
			BoundSql countBoundSql = new BoundSql(mappedStatement.getConfiguration(),countSql,boundSql.getParameterMappings(),boundSql.getParameterObject());
			// 构建ParameterHandler，用于设置统计sql的参数
			ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement,boundSql.getParameterObject(),countBoundSql);
			// 设置总数sql的参数
			parameterHandler.setParameters(countStmt);
			rs = countStmt.executeQuery();
			int totalCount = 0;
			if(rs.next()){
				totalCount = rs.getInt(1);
			}
			page.setTotal(totalCount);
			int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0) ? 0 : 1);
			page.setPages(totalPage);
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			closeStatement(countStmt);
		}
	}

	private void closeStatement(Statement... sts){
		for(Statement st:sts){
			try{
				st.close();
			}catch(Exception e){
			}
		}
	}

	/**
	 * 从代理对象中分离出真实对象.
	 *
	 * @return 非代理StatementHandler对象
	 */
	private StatementHandler getUnProxyObject(Invocation ivt){
		StatementHandler statementHandler = (StatementHandler)ivt.getTarget();
		MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
		// 分离代理对象链(由于目标类可能被多个拦截器拦截，从而形成多次代理，通过循环可以分离出最原始的的目标类)
		Object object = null;
		while(metaStatementHandler.hasGetter("h")){
			object = metaStatementHandler.getValue("h");
		}
		if(object == null){
			return statementHandler;
		}
		return (StatementHandler)object;
	}

	@Override
	public void setProperties(Properties props){
	}

	@Override
	public Object plugin(Object target){
		// 当目标类是StatementHandler类型时，才包装目标类，否者直接返回目标本身,减少目标被代理的
		if(target instanceof StatementHandler){
			return Plugin.wrap(target,this);
		}else{
			return target;
		}
	}
}
